﻿using System;
using System.Diagnostics;
using System.Net.Sockets;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
using HslCommunication.Core;
using HslCommunication.Core.Net;
using HslCommunication.LogNet;

namespace HslCommunication.WebSocket
{
	/// <summary>
	/// websocket协议的客户端实现，支持从服务器订阅，发布数据内容信息，详细参考api文档信息<br />
	/// Client implementation of the websocket protocol. It supports subscribing from the server and publishing data content information.
	/// </summary>
	/// <example>
	/// 本客户端使用起来非常的方便，基本就是实例化，绑定一个数据接收的事件即可，如下所示
	/// <code lang="cs" source="HslCommunication_Net45.Test\Documentation\Samples\WebSocket\WebSocketClientSample.cs" region="Sample1" title="简单的实例化" />
	/// 假设我们需要发数据给服务端，那么可以参考如下的方式
	/// <code lang="cs" source="HslCommunication_Net45.Test\Documentation\Samples\WebSocket\WebSocketClientSample.cs" region="Sample2" title="发送数据" />
	/// 如果我们需要搭配服务器来做订阅推送的功能的话，写法上会稍微有点区别，按照下面的代码来写。
	/// <code lang="cs" source="HslCommunication_Net45.Test\Documentation\Samples\WebSocket\WebSocketClientSample.cs" region="Sample3" title="订阅操作" />
	/// 当网络发生异常的时候，我们需要这么来进行重新连接。
	/// <code lang="cs" source="HslCommunication_Net45.Test\Documentation\Samples\WebSocket\WebSocketClientSample.cs" region="Sample4" title="异常重连" />
	/// </example>
	// Token: 0x02000021 RID: 33
	public class WebSocketClient : NetworkXBase
	{
		/// <summary>
		/// 使用指定的ip，端口来实例化一个默认的对象<br />
		/// Use the specified ip and port to instantiate a default objects
		/// </summary>
		/// <param name="ipAddress">Ip地址信息</param>
		/// <param name="port">端口号信息</param>
		// Token: 0x0600021B RID: 539 RVA: 0x00008050 File Offset: 0x00006250
		public WebSocketClient(string ipAddress, int port)
		{
			this.IpAddress = ipAddress;
			this.Port = port;
		}

		/// <summary>
		/// 使用指定的ip，端口，额外的url信息来实例化一个默认的对象<br />
		/// Use the specified ip, port, and additional url information to instantiate a default object
		/// </summary>
		/// <param name="ipAddress">Ip地址信息</param>
		/// <param name="port">端口号信息</param>
		/// <param name="url">额外的信息，比如 /A/B?C=123456</param>
		// Token: 0x0600021C RID: 540 RVA: 0x000080B0 File Offset: 0x000062B0
		public WebSocketClient(string ipAddress, int port, string url)
		{
			this.IpAddress = ipAddress;
			this.Port = port;
			this.url = url;
		}

		/// <summary>
		/// 使用指定的url来实例化一个默认的对象，例如 ws://127.0.0.1:1883/A/B?C=123456 或是 ws://www.hslcommunication.cn:1883<br />
		/// Use the specified url to instantiate a default object, such as ws://127.0.0.1:1883/A/B?C=123456 or ws://www.hslcommunication.cn:1883s
		/// </summary>
		/// <param name="url">完整的ws地址</param>
		// Token: 0x0600021D RID: 541 RVA: 0x00008118 File Offset: 0x00006318
		public WebSocketClient(string url)
		{
			bool flag = url.StartsWith("ws://");
			if (flag)
			{
				url = url.Substring(5);
				this.IpAddress = url.Substring(0, url.IndexOf(':'));
				url = url.Substring(url.IndexOf(':') + 1);
				bool flag2 = url.IndexOf('/') < 0;
				if (flag2)
				{
					this.Port = int.Parse(url);
				}
				else
				{
					this.Port = int.Parse(url.Substring(0, url.IndexOf('/')));
					this.url = url.Substring(url.IndexOf('/'));
				}
				return;
			}
			throw new Exception("Url Must start with ws://");
		}

		/// <summary>
		/// Mqtt服务器的ip地址<br />
		/// IP address of Mqtt server
		/// </summary>
		// Token: 0x17000080 RID: 128
		// (get) Token: 0x0600021E RID: 542 RVA: 0x0000820B File Offset: 0x0000640B
		// (set) Token: 0x0600021F RID: 543 RVA: 0x00008213 File Offset: 0x00006413
		public string IpAddress
		{
			get
			{
				return this.ipAddress;
			}
			set
			{
				this.ipAddress = HslHelper.GetIpAddressFromInput(value);
			}
		}

		/// <summary>
		/// 端口号。默认1883<br />
		/// The port number. Default 1883
		/// </summary>
		// Token: 0x17000081 RID: 129
		// (get) Token: 0x06000220 RID: 544 RVA: 0x00008222 File Offset: 0x00006422
		// (set) Token: 0x06000221 RID: 545 RVA: 0x0000822A File Offset: 0x0000642A
		public int Port
		{
			get
			{
				return this.port;
			}
			set
			{
				this.port = value;
			}
		}

		/// <summary>
		/// 连接服务器，实例化客户端之后，至少要调用成功一次，如果返回失败，那些请过一段时间后重新调用本方法连接。<br />
		/// After connecting to the server, the client must be called at least once after instantiating the client.
		/// If the return fails, please call this method to connect again after a period of time.
		/// </summary>
		/// <returns>连接是否成功</returns>
		// Token: 0x06000222 RID: 546 RVA: 0x00008233 File Offset: 0x00006433
		public OperateResult ConnectServer()
		{
			return this.ConnectServer(null);
		}

		/// <summary>
		/// 连接服务器，实例化客户端之后，至少要调用成功一次，如果返回失败，那些请过一段时间后重新调用本方法连接。<br />
		/// After connecting to the server, the client must be called at least once after instantiating the client.
		/// If the return fails, please call this method to connect again after a period of time.
		/// </summary>
		/// <param name="subscribes">订阅的消息</param>
		/// <returns>连接是否成功</returns>
		// Token: 0x06000223 RID: 547 RVA: 0x0000823C File Offset: 0x0000643C
		public OperateResult ConnectServer(string[] subscribes)
		{
			this.subcribeTopics = subscribes;
			Socket coreSocket = this.CoreSocket;
			if (coreSocket != null)
			{
				coreSocket.Close();
			}
			OperateResult<Socket> operateResult = base.CreateSocketAndConnect(this.ipAddress, this.port, this.connectTimeOut);
			bool flag = !operateResult.IsSuccess;
			OperateResult result;
			if (flag)
			{
				result = operateResult;
			}
			else
			{
				this.CoreSocket = operateResult.Content;
				byte[] data = WebSocketHelper.BuildWsSubRequest(this.ipAddress, this.port, this.url, this.subcribeTopics);
				OperateResult operateResult2 = base.Send(this.CoreSocket, data);
				bool flag2 = !operateResult2.IsSuccess;
				if (flag2)
				{
					result = operateResult2;
				}
				else
				{
					OperateResult<byte[]> operateResult3 = base.Receive(this.CoreSocket, -1, 10000, null);
					bool flag3 = !operateResult3.IsSuccess;
					if (flag3)
					{
						result = operateResult3;
					}
					else
					{
						try
						{
							this.CoreSocket.BeginReceive(new byte[0], 0, 0, SocketFlags.None, new AsyncCallback(this.ReceiveAsyncCallback), this.CoreSocket);
						}
						catch (Exception ex)
						{
							return new OperateResult(ex.Message);
						}
						this.closed = false;
						WebSocketClient.OnClientConnectedDelegate onClientConnected = this.OnClientConnected;
						if (onClientConnected != null)
						{
							onClientConnected();
						}
						Timer timer = this.timerCheck;
						if (timer != null)
						{
							timer.Dispose();
						}
						this.timerCheck = new Timer(new TimerCallback(this.TimerCheckServer), null, 2000, 30000);
						result = OperateResult.CreateSuccessResult();
					}
				}
			}
			return result;
		}

		/// <summary>
		/// 关闭Mqtt服务器的连接。<br />
		/// Close the connection to the Mqtt server.
		/// </summary>
		// Token: 0x06000224 RID: 548 RVA: 0x000083B4 File Offset: 0x000065B4
		public void ConnectClose()
		{
			bool flag = !this.closed;
			if (flag)
			{
				base.Send(this.CoreSocket, WebSocketHelper.WebScoketPackData(8, true, "Closed"));
				this.closed = true;
				Thread.Sleep(20);
				Socket coreSocket = this.CoreSocket;
				if (coreSocket != null)
				{
					coreSocket.Close();
				}
			}
		}

		/// <inheritdoc cref="M:HslCommunication.WebSocket.WebSocketClient.ConnectServer" />
		// Token: 0x06000225 RID: 549 RVA: 0x0000840C File Offset: 0x0000660C
		[DebuggerStepThrough]
		public Task<OperateResult> ConnectServerAsync()
		{
			WebSocketClient.<ConnectServerAsync>d__12 <ConnectServerAsync>d__ = new WebSocketClient.<ConnectServerAsync>d__12();
			<ConnectServerAsync>d__.<>t__builder = AsyncTaskMethodBuilder<OperateResult>.Create();
			<ConnectServerAsync>d__.<>4__this = this;
			<ConnectServerAsync>d__.<>1__state = -1;
			<ConnectServerAsync>d__.<>t__builder.Start<WebSocketClient.<ConnectServerAsync>d__12>(ref <ConnectServerAsync>d__);
			return <ConnectServerAsync>d__.<>t__builder.Task;
		}

		/// <inheritdoc cref="M:HslCommunication.WebSocket.WebSocketClient.ConnectServer(System.String[])" />
		// Token: 0x06000226 RID: 550 RVA: 0x00008450 File Offset: 0x00006650
		[DebuggerStepThrough]
		public Task<OperateResult> ConnectServerAsync(string[] subscribes)
		{
			WebSocketClient.<ConnectServerAsync>d__13 <ConnectServerAsync>d__ = new WebSocketClient.<ConnectServerAsync>d__13();
			<ConnectServerAsync>d__.<>t__builder = AsyncTaskMethodBuilder<OperateResult>.Create();
			<ConnectServerAsync>d__.<>4__this = this;
			<ConnectServerAsync>d__.subscribes = subscribes;
			<ConnectServerAsync>d__.<>1__state = -1;
			<ConnectServerAsync>d__.<>t__builder.Start<WebSocketClient.<ConnectServerAsync>d__13>(ref <ConnectServerAsync>d__);
			return <ConnectServerAsync>d__.<>t__builder.Task;
		}

		/// <inheritdoc cref="M:HslCommunication.WebSocket.WebSocketClient.ConnectClose" />
		// Token: 0x06000227 RID: 551 RVA: 0x0000849C File Offset: 0x0000669C
		[DebuggerStepThrough]
		public Task ConnectCloseAsync()
		{
			WebSocketClient.<ConnectCloseAsync>d__14 <ConnectCloseAsync>d__ = new WebSocketClient.<ConnectCloseAsync>d__14();
			<ConnectCloseAsync>d__.<>t__builder = AsyncTaskMethodBuilder.Create();
			<ConnectCloseAsync>d__.<>4__this = this;
			<ConnectCloseAsync>d__.<>1__state = -1;
			<ConnectCloseAsync>d__.<>t__builder.Start<WebSocketClient.<ConnectCloseAsync>d__14>(ref <ConnectCloseAsync>d__);
			return <ConnectCloseAsync>d__.<>t__builder.Task;
		}

		// Token: 0x06000228 RID: 552 RVA: 0x000084E0 File Offset: 0x000066E0
		private void OnWebsocketNetworkError()
		{
			bool flag = this.closed;
			if (!flag)
			{
				bool flag2 = Interlocked.CompareExchange(ref this.isReConnectServer, 1, 0) == 0;
				if (flag2)
				{
					try
					{
						bool flag3 = this.OnNetworkError == null;
						if (flag3)
						{
							ILogNet logNet = base.LogNet;
							if (logNet != null)
							{
								logNet.WriteInfo(this.ToString(), "The network is abnormal, and the system is ready to automatically reconnect after 10 seconds.");
							}
							for (;;)
							{
								for (int i = 0; i < 10; i++)
								{
									bool flag4 = this.closed;
									if (flag4)
									{
										goto Block_6;
									}
									Thread.Sleep(1000);
									ILogNet logNet2 = base.LogNet;
									if (logNet2 != null)
									{
										logNet2.WriteInfo(this.ToString(), string.Format("Wait for {0} second to connect to the server ...", 10 - i));
									}
								}
								bool flag5 = this.closed;
								if (flag5)
								{
									goto Block_9;
								}
								OperateResult operateResult = this.ConnectServer();
								bool isSuccess = operateResult.IsSuccess;
								if (isSuccess)
								{
									goto Block_10;
								}
								ILogNet logNet3 = base.LogNet;
								if (logNet3 != null)
								{
									logNet3.WriteInfo(this.ToString(), "The connection failed. Prepare to reconnect after 10 seconds.");
								}
							}
							Block_6:
							Interlocked.Exchange(ref this.isReConnectServer, 0);
							return;
							Block_9:
							Interlocked.Exchange(ref this.isReConnectServer, 0);
							return;
							Block_10:
							ILogNet logNet4 = base.LogNet;
							if (logNet4 != null)
							{
								logNet4.WriteInfo(this.ToString(), "Successfully connected to the server!");
							}
						}
						else
						{
							EventHandler onNetworkError = this.OnNetworkError;
							if (onNetworkError != null)
							{
								onNetworkError(this, new EventArgs());
							}
						}
						Interlocked.Exchange(ref this.isReConnectServer, 0);
					}
					catch
					{
						Interlocked.Exchange(ref this.isReConnectServer, 0);
						throw;
					}
				}
			}
		}

		// Token: 0x06000229 RID: 553 RVA: 0x0000868C File Offset: 0x0000688C
		[DebuggerStepThrough]
		private void ReceiveAsyncCallback(IAsyncResult ar)
		{
			WebSocketClient.<ReceiveAsyncCallback>d__16 <ReceiveAsyncCallback>d__ = new WebSocketClient.<ReceiveAsyncCallback>d__16();
			<ReceiveAsyncCallback>d__.<>t__builder = AsyncVoidMethodBuilder.Create();
			<ReceiveAsyncCallback>d__.<>4__this = this;
			<ReceiveAsyncCallback>d__.ar = ar;
			<ReceiveAsyncCallback>d__.<>1__state = -1;
			<ReceiveAsyncCallback>d__.<>t__builder.Start<WebSocketClient.<ReceiveAsyncCallback>d__16>(ref <ReceiveAsyncCallback>d__);
		}

		// Token: 0x0600022A RID: 554 RVA: 0x000086CC File Offset: 0x000068CC
		private void TimerCheckServer(object obj)
		{
			bool flag = this.CoreSocket != null;
			if (flag)
			{
			}
		}

		/// <summary>
		/// 发送数据到WebSocket的服务器<br />
		/// Send data to WebSocket server
		/// </summary>
		/// <param name="message">消息</param>
		/// <returns>是否发送成功</returns>
		// Token: 0x0600022B RID: 555 RVA: 0x000086EC File Offset: 0x000068EC
		public OperateResult SendServer(string message)
		{
			return base.Send(this.CoreSocket, WebSocketHelper.WebScoketPackData(1, true, message));
		}

		/// <summary>
		/// websocket的消息收到时触发<br />
		/// Triggered when a websocket message is received
		///             </summary>
		// Token: 0x14000001 RID: 1
		// (add) Token: 0x0600022C RID: 556 RVA: 0x00008714 File Offset: 0x00006914
		// (remove) Token: 0x0600022D RID: 557 RVA: 0x0000874C File Offset: 0x0000694C
		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public event WebSocketClient.OnClientApplicationMessageReceiveDelegate OnClientApplicationMessageReceive;

		/// <summary>
		/// 当客户端连接成功触发事件，就算是重新连接服务器后，也是会触发的<br />
		/// The event is triggered when the client is connected successfully, even after reconnecting to the server.
		/// </summary>
		// Token: 0x14000002 RID: 2
		// (add) Token: 0x0600022E RID: 558 RVA: 0x00008784 File Offset: 0x00006984
		// (remove) Token: 0x0600022F RID: 559 RVA: 0x000087BC File Offset: 0x000069BC
		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public event WebSocketClient.OnClientConnectedDelegate OnClientConnected;

		/// <summary>
		/// 当网络发生异常的时候触发的事件，用户应该在事件里进行重连服务器
		/// </summary>
		// Token: 0x14000003 RID: 3
		// (add) Token: 0x06000230 RID: 560 RVA: 0x000087F4 File Offset: 0x000069F4
		// (remove) Token: 0x06000231 RID: 561 RVA: 0x0000882C File Offset: 0x00006A2C
		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public event EventHandler OnNetworkError;

		/// <summary>
		/// 获取或设置当前客户端的连接超时时间，默认10,000毫秒，单位ms<br />
		/// Gets or sets the connection timeout of the current client. The default is 10,000 milliseconds. The unit is ms.
		/// </summary>
		// Token: 0x17000082 RID: 130
		// (get) Token: 0x06000232 RID: 562 RVA: 0x00008861 File Offset: 0x00006A61
		// (set) Token: 0x06000233 RID: 563 RVA: 0x00008869 File Offset: 0x00006A69
		public int ConnectTimeOut
		{
			get
			{
				return this.connectTimeOut;
			}
			set
			{
				this.connectTimeOut = value;
			}
		}

		/// <summary>
		/// 获取当前的客户端状态是否关闭了连接，当自己手动处理网络异常事件的时候，在重连之前就需要判断是否关闭了连接。<br />
		/// Obtain whether the current client status has closed the connection. When manually handling network abnormal events, you need to determine whether the connection is closed before reconnecting.
		/// </summary>
		// Token: 0x17000083 RID: 131
		// (get) Token: 0x06000234 RID: 564 RVA: 0x00008872 File Offset: 0x00006A72
		public bool IsClosed
		{
			get
			{
				return this.closed;
			}
		}

		/// <inheritdoc />
		// Token: 0x06000235 RID: 565 RVA: 0x0000887A File Offset: 0x00006A7A
		public override string ToString()
		{
			return string.Format("WebSocketClient[{0}:{1}]", this.ipAddress, this.port);
		}

		// Token: 0x040000B2 RID: 178
		private int isReConnectServer = 0;

		// Token: 0x040000B3 RID: 179
		private string[] subcribeTopics;

		// Token: 0x040000B4 RID: 180
		private bool closed = false;

		// Token: 0x040000B5 RID: 181
		private string ipAddress = string.Empty;

		// Token: 0x040000B6 RID: 182
		private int port = 1883;

		// Token: 0x040000B7 RID: 183
		private int connectTimeOut = 10000;

		// Token: 0x040000B8 RID: 184
		private Timer timerCheck;

		// Token: 0x040000B9 RID: 185
		private string url = string.Empty;

		/// <summary>
		/// websocket的消息收到委托<br />
		/// websocket message received delegate
		/// </summary>
		/// <param name="message">websocket的消息</param>
		// Token: 0x020001AD RID: 429
		// (Invoke) Token: 0x0600216D RID: 8557
		public delegate void OnClientApplicationMessageReceiveDelegate(WebSocketMessage message);

		/// <summary>
		/// 连接服务器成功的委托<br />
		/// Connection server successfully delegated
		/// </summary>
		// Token: 0x020001AE RID: 430
		// (Invoke) Token: 0x06002171 RID: 8561
		public delegate void OnClientConnectedDelegate();
	}
}
